package cc.rboot.fsm.core;

import cc.rboot._core.helper.ClassHelper;
import cc.rboot._core.holder.AppContextHolder;
import cc.rboot.fsm.error.FsmNoDefaultException;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class AbstractFsmService<T, S> implements IFsmService<T> {

    private final Map<T, IFsmState<T>> STATE_MAP = new ConcurrentHashMap<>();

    private IFsmState<T> defaultState;

    /**
     * 此处就是要用实例，不是写错了。
     * 如果不这样写，因为bean加载顺序问题，可能导致找不到bean
     */
    @Resource
    AppContextHolder appContextHolder;

    /**
     * 初始化
     */
    @PostConstruct
    public void construct() {
        // OrderFsmService extends AbstractFsmService<Integer, OrderFsmState>
        Class beanClass = this.getClass(); // OrderFsmService

        Class<? extends IFsmState<T>> clazz = (Class<? extends IFsmState<T>>) ClassHelper.getTypeArgument(beanClass, 1);

        appContextHolder.getBeanMap(clazz).forEach((Key, fsmState) -> {
            STATE_MAP.put(fsmState.code(), fsmState);
        });

        Class<? extends IFsmState<T>> stateClass = this.defaultStateClass();
        this.defaultState = appContextHolder.getBean(stateClass);
    }

    /**
     * 获取code所指的状态
     *
     * @param code
     * @return
     * @throws Exception
     */
    public IFsmState<T> currentState(T code) {
        if (code == null) {
            throw new RuntimeException("code不能为空");
        }
        if (this.STATE_MAP.containsKey(code)) {
            return this.STATE_MAP.get(code);
        }
        throw new RuntimeException("未定义该状态值");
    }

    /**
     * 从code所指的状态转换到下一个状态，操作是event
     */
    @Override
    public IFsmState<T> transfer(T code, Class<? extends IFsmEvent> eventClass, Object ... params) {
        return this.currentState(code).transfer(eventClass, params);
    }

    public IFsmState<T> getDefaultState() throws Exception {
        if (this.defaultState == null) {
            throw new FsmNoDefaultException();
        }
        return defaultState;
    }
}
